Popular Searches
Popular Course Categories
Popular Courses

Building Bottom Navigation and Navigation Drawers

Building Bottom Navigation and Navigation Drawers

User Interaction, Forms, and Navigation

Building Bottom Navigation and Navigation Drawers in Android Using Jetpack Compose

Course: Android App Development

Topic: Building Bottom Navigation and Navigation Drawers

Course Link: Android App Development Course

Demo Link: Register for a Free Course Demo

JustAcademy Website: Visit JustAcademy


1. Introduction to Navigation in Android

Navigation is an essential part of Android application development. It allows users to move between different screens, sections, and features of an application without confusion. For example, a shopping application may provide Home, Categories, Cart, and Profile screens, while a learning application may contain Courses, Lessons, Downloads, and Settings.

In Jetpack Compose, navigation interfaces can be built using declarative composable functions. Material 3 provides components such as NavigationBar, NavigationBarItem, ModalNavigationDrawer, and NavigationDrawerItem to create familiar navigation experiences.

Bottom navigation is suitable for switching between a small number of frequently used destinations. A navigation drawer is useful when an application contains more sections than can comfortably fit in a bottom bar. These components can also be combined when the design and screen size justify it.

Learning Objectives

  • Understand navigation patterns in Android applications.
  • Learn the difference between bottom navigation and navigation drawers.
  • Build a bottom navigation bar using Jetpack Compose Material 3.
  • Create and control a modal navigation drawer.
  • Manage selected navigation items and UI state.
  • Connect navigation interfaces with application screens.
  • Apply accessibility, usability, and responsive design principles.
  • Identify common navigation implementation errors and fix them.

2. What Is Bottom Navigation?

Bottom navigation is a horizontal navigation component positioned near the bottom of the application window. It provides direct access to a few important destinations, allowing users to switch between sections with minimal effort.

For example, a music application might provide the following destinations:

  • Home: Displays recommendations and recently played content.
  • Search: Allows users to find songs or artists.
  • Library: Displays saved albums and playlists.
  • Profile: Provides account information and preferences.

2.1 Main Characteristics

  • Appears at the bottom of the application interface.
  • Usually contains three to five destinations of similar importance.
  • Can display icons and text labels.
  • Highlights the currently selected destination.
  • Works well on compact mobile screens.
  • Allows users to switch between major sections quickly.

2.2 Advantages of Bottom Navigation

Advantage Explanation
Easy access Important sections remain visible without opening a menu.
Discoverability Users can immediately see the main areas of the application.
Quick switching Users can change sections with a single tap.
Mobile-friendly Controls are positioned within convenient reach on many phones.
Visual feedback The selected item can be highlighted to communicate the current section.

3. What Is a Navigation Drawer?

A navigation drawer is a panel containing navigation links that slides in from the side of the screen or remains visible in certain larger-screen layouts. It can organize multiple destinations, grouped sections, account information, and secondary application features.

For example, a learning application might use a drawer with Dashboard, My Courses, Certificates, Downloads, Help, and Settings.

3.1 Common Types of Navigation Drawers

Type Description Typical Usage
Modal navigation drawer Appears over the main content and can be dismissed. Mobile applications and compact layouts.
Dismissible navigation drawer Can be shown or hidden alongside the main content. Medium-width layouts where screen space is shared.
Permanent navigation drawer Remains visible alongside the main content. Large tablets, desktop-style interfaces, and wide layouts.

3.2 Advantages of Navigation Drawers

  • Support more destinations than a typical bottom navigation bar.
  • Allow related options to be organized into groups.
  • Can include profile details, section headings, icons, and dividers.
  • Help keep the primary screen uncluttered.
  • Adapt well to applications with many features.

4. Bottom Navigation vs. Navigation Drawer

Feature Bottom Navigation Navigation Drawer
Position Bottom of the screen Usually slides in from a screen edge
Best for A few equally important destinations Many destinations or grouped options
Visibility Usually visible at all times on the relevant screens Often hidden until opened
Interaction Tap a visible navigation item Tap a menu icon, swipe where supported, or use a persistent panel
Screen space Uses space at the bottom A modal drawer overlays content; other drawer types share screen space
Typical example Home, Search, Saved Dashboard, Reports, Downloads, Settings, Help

Important: Choose the navigation pattern based on the number and importance of destinations, the available screen size, and the needs of your users. Do not add both patterns automatically if doing so creates duplicate or confusing navigation.


5. Prerequisites and Project Setup

Before creating navigation components, ensure that your Android project uses Kotlin, Jetpack Compose, and Material 3.

5.1 Required Concepts

  • Kotlin variables, functions, lambdas, and data classes.
  • Composable functions and the @Composable annotation.
  • State management with remember and mutableStateOf.
  • Layout components such as Scaffold, Column, and Row.
  • Material 3 UI components and icons.

5.2 Dependencies

Use the Compose and Material 3 dependencies configured for your project. If you use a version catalog, the dependency aliases may differ from the example below.

dependencies {
    implementation("androidx.compose.material3:material3:<compatible-version>")
    implementation("androidx.compose.material:material-icons-extended:<compatible-version>")
}

Replace the placeholder versions with compatible versions from your project's version catalog or official Android documentation. The extended icon library is optional if you use icons available from another dependency.

If you want actual screen-to-screen navigation using a navigation graph, add the Navigation Compose dependency as well:

dependencies {
    implementation("androidx.navigation:navigation-compose:<compatible-version>")
}

Do not copy placeholder version strings directly into a production build. Use versions compatible with your Android Gradle Plugin and Compose setup.


6. Understanding Important Bottom Navigation Components

Component or Property Purpose
NavigationBar Creates the bottom navigation container.
NavigationBarItem Represents one destination in the bar.
selected Indicates whether an item is currently selected.
onClick Handles a user's selection of an item.
icon Provides a visual symbol for the destination.
label Displays the destination's text label.
Scaffold Provides a standard layout structure for bars and screen content.

7. Creating a Simple Bottom Navigation Bar

The following example creates a bottom bar with Home, Search, and Profile destinations. The selected destination is stored in Compose state, and the central content changes when the user selects an item.

7.1 Complete Example

import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Home
import androidx.compose.material.icons.filled.Person
import androidx.compose.material.icons.filled.Search
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.NavigationBar
import androidx.compose.material3.NavigationBarItem
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier

@Composable
fun BottomNavigationExample() {
    var selectedIndex by rememberSaveable {
        mutableIntStateOf(0)
    }

    val destinations = listOf("Home", "Search", "Profile")
    val icons = listOf(
        Icons.Filled.Home,
        Icons.Filled.Search,
        Icons.Filled.Person
    )

    Scaffold(
        bottomBar = {
            NavigationBar {
                destinations.forEachIndexed { index, title ->
                    NavigationBarItem(
                        selected = selectedIndex == index,
                        onClick = {
                            selectedIndex = index
                        },
                        icon = {
                            Icon(
                                imageVector = icons[index],
                                contentDescription = title
                            )
                        },
                        label = {
                            Text(title)
                        }
                    )
                }
            }
        }
    ) { innerPadding ->
        Column(
            modifier = Modifier
                .fillMaxSize()
                .padding(innerPadding),
            horizontalAlignment = Alignment.CenterHorizontally,
            verticalArrangement = Arrangement.Center
        ) {
            Text(
                text = destinations[selectedIndex],
                style = MaterialTheme.typography.headlineMedium
            )
            Text("Selected application section")
        }
    }
}

Import note: Add import androidx.compose.foundation.layout.padding for the padding modifier. Ensure the icon dependency is available in the project.

7.2 How the Code Works

  1. rememberSaveable stores the selected index and can restore it across supported activity recreation scenarios.
  2. The destinations list stores the text labels.
  3. The icons list stores an icon for each destination.
  4. Scaffold defines the screen structure and provides a dedicated bottomBar slot.
  5. NavigationBar displays the navigation items horizontally.
  6. forEachIndexed creates one navigation item for each destination.
  7. selectedIndex == index determines which item appears selected.
  8. The onClick callback updates the selected index.
  9. The main content displays the name of the selected section.

Learning point: This example switches the displayed content using local state. It does not create separate navigation destinations or maintain a navigation back stack. For a larger application, connect the items to Navigation Compose or another clearly defined navigation architecture.


8. Managing Bottom Navigation State

State determines which destination is selected. When a user taps an item, Compose updates the state and recomposes the affected UI.

8.1 Using remember

var selectedTab by remember {
    mutableIntStateOf(0)
}

remember retains the value during recomposition, but does not by itself preserve it across activity recreation.

8.2 Using rememberSaveable

var selectedTab by rememberSaveable {
    mutableIntStateOf(0)
}

rememberSaveable can preserve supported state through activity recreation and saved-instance-state restoration. It is useful for simple UI values such as the currently selected tab.

8.3 State Management Best Practices

  • Keep selected state in the lowest common composable that needs it.
  • Use stable route names rather than relying on list positions for larger navigation graphs.
  • Separate UI state from business logic when the application becomes complex.
  • Ensure the displayed content and highlighted navigation item always agree.
  • Do not create duplicate state variables for the same selected destination.

9. Introduction to Navigation Drawers in Jetpack Compose

A navigation drawer provides a panel of destinations that users can access from the side of the application. In Material 3, ModalNavigationDrawer is commonly used for a drawer that overlays the current screen.

Its main building blocks include:

  • ModalNavigationDrawer — wraps the main screen and defines the drawer container.
  • drawerContent — supplies the content displayed inside the drawer.
  • ModalDrawerSheet — provides the drawer's surface and styling.
  • NavigationDrawerItem — represents an individual destination.
  • rememberDrawerState — remembers whether the drawer is open or closed.
  • rememberCoroutineScope — provides a coroutine scope for suspend operations triggered by UI events.

10. Creating a Basic Navigation Drawer

The following example builds a modal drawer with Dashboard, Courses, Downloads, and Settings. Selecting an item updates the current section and closes the drawer.

10.1 Complete Example

import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Dashboard
import androidx.compose.material.icons.filled.Download
import androidx.compose.material.icons.filled.MenuBook
import androidx.compose.material.icons.filled.Settings
import androidx.compose.material3.DrawerValue
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ModalDrawerSheet
import androidx.compose.material3.ModalNavigationDrawer
import androidx.compose.material3.NavigationDrawerItem
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.IconButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.material3.rememberDrawerState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import kotlinx.coroutines.launch

@Composable
fun NavigationDrawerExample() {
    val drawerState = rememberDrawerState(
        initialValue = DrawerValue.Closed
    )
    val scope = rememberCoroutineScope()

    val destinations = listOf(
        "Dashboard",
        "Courses",
        "Downloads",
        "Settings"
    )

    var selectedDestination by rememberSaveable {
        mutableStateOf("Dashboard")
    }

    ModalNavigationDrawer(
        drawerState = drawerState,
        drawerContent = {
            ModalDrawerSheet {
                Text(
                    text = "Learning App",
                    style = MaterialTheme.typography.headlineSmall,
                    modifier = Modifier.padding(16.dp)
                )

                HorizontalDivider()

                NavigationDrawerItem(
                    label = { Text("Dashboard") },
                    selected = selectedDestination == "Dashboard",
                    icon = {
                        Icon(
                            Icons.Filled.Dashboard,
                            contentDescription = null
                        )
                    },
                    onClick = {
                        selectedDestination = "Dashboard"
                        scope.launch {
                            drawerState.close()
                        }
                    },
                    modifier = Modifier.padding(
                        horizontal = 12.dp
                    )
                )

                NavigationDrawerItem(
                    label = { Text("Courses") },
                    selected = selectedDestination == "Courses",
                    icon = {
                        Icon(
                            Icons.Filled.MenuBook,
                            contentDescription = null
                        )
                    },
                    onClick = {
                        selectedDestination = "Courses"
                        scope.launch {
                            drawerState.close()
                        }
                    },
                    modifier = Modifier.padding(
                        horizontal = 12.dp
                    )
                )

                NavigationDrawerItem(
                    label = { Text("Downloads") },
                    selected = selectedDestination == "Downloads",
                    icon = {
                        Icon(
                            Icons.Filled.Download,
                            contentDescription = null
                        )
                    },
                    onClick = {
                        selectedDestination = "Downloads"
                        scope.launch {
                            drawerState.close()
                        }
                    },
                    modifier = Modifier.padding(
                        horizontal = 12.dp
                    )
                )

                NavigationDrawerItem(
                    label = { Text("Settings") },
                    selected = selectedDestination == "Settings",
                    icon = {
                        Icon(
                            Icons.Filled.Settings,
                            contentDescription = null
                        )
                    },
                    onClick = {
                        selectedDestination = "Settings"
                        scope.launch {
                            drawerState.close()
                        }
                    },
                    modifier = Modifier.padding(
                        horizontal = 12.dp
                    )
                )
            }
        }
    ) {
        Scaffold(
            topBar = {
                TopAppBar(
                    title = {
                        Text(selectedDestination)
                    },
                    navigationIcon = {
                        IconButton(
                            onClick = {
                                scope.launch {
                                    if (drawerState.isClosed) {
                                        drawerState.open()
                                    } else {
                                        drawerState.close()
                                    }
                                }
                            }
                        ) {
                            Icon(
                                imageVector = Icons.Filled.MenuBook,
                                contentDescription = "Open navigation menu"
                            )
                        }
                    }
                )
            }
        ) { innerPadding ->
            Column(
                modifier = Modifier
                    .fillMaxSize()
                    .padding(innerPadding),
                horizontalAlignment = Alignment.CenterHorizontally,
                verticalArrangement = Arrangement.Center
            ) {
                Text(
                    text = selectedDestination,
                    style = MaterialTheme.typography.headlineMedium
                )
                Text("Current application section")
            }
        }
    }
}

Implementation notes:

  • For a standard hamburger menu, use a menu icon such as Icons.Filled.Menu if it is available in the icon dependency you selected. Replace the example's navigation icon with that menu icon for a clearer visual cue.
  • Some icon sets or dependency versions may not provide every icon shown in examples. If an import cannot be resolved, choose an available equivalent or add the appropriate icon dependency.
  • Remove the unused destinations list and unused imports when copying this example into your project; they are not needed by the displayed implementation.
  • Depending on the Material 3 version, TopAppBar may require the appropriate experimental API opt-in. Follow the API requirements of the version installed in your project.

10.2 Explanation of the Drawer Code

  1. rememberDrawerState creates state that tracks whether the drawer is open or closed.
  2. ModalNavigationDrawer wraps the application screen.
  3. The drawerContent lambda defines the panel and its navigation items.
  4. ModalDrawerSheet provides the drawer surface.
  5. NavigationDrawerItem displays a label, selection state, optional icon, and click callback.
  6. rememberCoroutineScope provides a scope for calling the suspending open() and close() functions.
  7. When an item is clicked, the selected destination is updated and the drawer is closed.
  8. The top app bar's navigation icon opens or closes the drawer.

11. Understanding DrawerState and CoroutineScope

Opening and closing a Material 3 drawer involves suspend functions. These functions should be called from a coroutine rather than directly inside a normal click callback.

11.1 Opening the Drawer

val drawerState = rememberDrawerState(
    initialValue = DrawerValue.Closed
)
val scope = rememberCoroutineScope()

IconButton(
    onClick = {
        scope.launch {
            drawerState.open()
        }
    }
) {
    Text("Open Menu")
}

11.2 Closing the Drawer

scope.launch {
    drawerState.close()
}

11.3 Toggling the Drawer

scope.launch {
    if (drawerState.isClosed) {
        drawerState.open()
    } else {
        drawerState.close()
    }
}

Use the same remembered drawer state for the drawer container and controls that open or close it. Creating unrelated drawer states can cause the menu button and visible drawer to behave inconsistently.


12. Combining Bottom Navigation and a Navigation Drawer

Some applications need both a bottom navigation bar and a drawer. For example, a learning app may use bottom navigation for Home, My Learning, and Profile, while the drawer provides Help, Certificates, Downloads, and Settings.

When combining these patterns, define a clear hierarchy:

  • Use the bottom bar for the few main destinations users visit frequently.
  • Use the drawer for secondary or less frequently used sections.
  • Avoid presenting the same destination in both places unless there is a clear usability reason.
  • Keep the selected state consistent with the currently displayed screen.
  • Use the same navigation architecture to manage destinations and back-stack behavior.

12.1 Suggested Application Structure

MyAndroidApp
|
|-- Scaffold
|   |-- TopAppBar
|   |   |-- Menu Button
|   |
|   |-- Main Content
|   |   |-- Home Screen
|   |   |-- My Learning Screen
|   |   |-- Profile Screen
|   |
|   |-- NavigationBar
|       |-- Home
|       |-- My Learning
|       |-- Profile
|
|-- ModalNavigationDrawer
    |-- Help
    |-- Certificates
    |-- Downloads
    |-- Settings

This is a conceptual structure, not a literal Kotlin declaration. In an actual project, the drawer and scaffold should be composed in an order that allows the drawer to wrap the main content, and navigation should be coordinated through shared state or a navigation controller.


13. Connecting Navigation Items to Real Screens

Updating a selected label is sufficient for a small demonstration, but production applications commonly use Navigation Compose to navigate between separate composable destinations.

Navigation Compose provides a navigation controller, a navigation host, and a graph of destinations. The controller manages navigation actions and the back stack, while the host displays the destination associated with the current route.

13.1 Define Routes

object AppRoutes {
    const val HOME = "home"
    const val SEARCH = "search"
    const val PROFILE = "profile"
}

13.2 Create a Navigation Host

import androidx.compose.runtime.Composable
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController

@Composable
fun AppNavigation() {
    val navController = rememberNavController()

    NavHost(
        navController = navController,
        startDestination = AppRoutes.HOME
    ) {
        composable(AppRoutes.HOME) {
            HomeScreen()
        }

        composable(AppRoutes.SEARCH) {
            SearchScreen()
        }

        composable(AppRoutes.PROFILE) {
            ProfileScreen()
        }
    }
}

In this example, HomeScreen(), SearchScreen(), and ProfileScreen() represent composable screen functions that you define separately.

13.3 Navigate When a Bottom Item Is Selected

NavigationBarItem(
    selected = currentRoute == AppRoutes.HOME,
    onClick = {
        navController.navigate(AppRoutes.HOME) {
            launchSingleTop = true
            restoreState = true
            popUpTo(navController.graph.startDestinationId) {
                saveState = true
            }
        }
    },
    icon = {
        Icon(
            imageVector = Icons.Filled.Home,
            contentDescription = "Home"
        )
    },
    label = {
        Text("Home")
    }
)

Important: This snippet assumes that navController and currentRoute are available in the surrounding composable. The complete implementation must observe the current back-stack entry and derive currentRoute from it. Apply the same navigation options consistently across destinations when you want tab state to be saved and restored.

13.4 Navigation from a Drawer Item

NavigationDrawerItem(
    label = {
        Text("Profile")
    },
    selected = currentRoute == AppRoutes.PROFILE,
    onClick = {
        navController.navigate(AppRoutes.PROFILE) {
            launchSingleTop = true
        }

        scope.launch {
            drawerState.close()
        }
    }
)

This approach separates visual navigation controls from actual screen navigation. The navigation controller changes the destination, and the drawer state handles the menu's visibility.


14. Creating a Reusable Bottom Navigation Component

Reusable components reduce duplication and make navigation easier to maintain. Instead of writing a separate item block for each destination, define a destination model and render the items from a list.

import androidx.compose.ui.graphics.vector.ImageVector

data class BottomDestination(
    val title: String,
    val route: String,
    val icon: ImageVector
)

You can then create a composable that accepts the destination list, the selected route, and a callback:

@Composable
fun AppBottomBar(
    destinations: List<BottomDestination>,
    selectedRoute: String,
    onDestinationSelected: (String) -> Unit
) {
    NavigationBar {
        destinations.forEach { destination ->
            NavigationBarItem(
                selected = selectedRoute == destination.route,
                onClick = {
                    onDestinationSelected(destination.route)
                },
                icon = {
                    Icon(
                        imageVector = destination.icon,
                        contentDescription = destination.title
                    )
                },
                label = {
                    Text(destination.title)
                }
            )
        }
    }
}

This component does not own the navigation controller. The parent composable supplies the current route and decides what to do when an item is selected. This makes the component reusable and easier to test.


15. Improving the Navigation Drawer Design

A well-designed drawer should make its destinations easy to understand. Avoid using the drawer as a random collection of links.

15.1 Add Section Headings

For applications with many options, organize related items into meaningful groups such as Learning, Account, and Support.

Text(
    text = "Learning",
    style = MaterialTheme.typography.titleSmall,
    modifier = Modifier.padding(16.dp)
)

NavigationDrawerItem(
    label = { Text("My Courses") },
    selected = false,
    onClick = { /* Navigate to My Courses */ }
)

NavigationDrawerItem(
    label = { Text("Certificates") },
    selected = false,
    onClick = { /* Navigate to Certificates */ }
)

HorizontalDivider(
    modifier = Modifier.padding(vertical = 8.dp)
)

15.2 Add a Profile Header

A profile header can display an avatar, a user's name, and an account description. Use meaningful accessibility descriptions for images and avoid displaying private information unnecessarily.

15.3 Support Long Drawers

If a drawer contains many entries, make its content scrollable. Keep important destinations easy to find and avoid excessively long labels.

15.4 Highlight the Current Destination

Set the selected parameter based on the current navigation destination rather than always using false. This provides consistent visual feedback when users reopen the drawer.


16. Accessibility and Usability Best Practices

  • Use clear labels: Prefer familiar terms such as Home, Search, Profile, and Settings.
  • Describe meaningful icons: Provide appropriate content descriptions when icons convey information not already expressed by nearby text.
  • Avoid redundant announcements: When an icon is decorative and its label already communicates the same information, a null content description may be appropriate.
  • Use visible selection states: Users should be able to identify the current destination.
  • Maintain adequate touch targets: Navigation controls should be easy to activate.
  • Respect system insets: Use Material components and Scaffold padding appropriately so navigation elements do not overlap system bars or content.
  • Support back navigation: Ensure screen transitions behave predictably when users press Back.
  • Test with accessibility tools: Check screen-reader labels, focus order, and contrast.
  • Do not rely on color alone: Use icons, labels, or other visual cues to indicate selection.
  • Consider large screens: A navigation rail or permanent drawer may work better than a bottom bar on wide layouts.

17. Responsive Navigation for Different Screen Sizes

Navigation design should adapt to the amount of available space. A bottom navigation bar is often effective on phones, while a navigation rail or permanent drawer can be more appropriate on tablets and desktop-style devices.

Screen Context Possible Navigation Pattern Reason
Compact phone Bottom navigation or modal drawer Provides direct access while preserving limited screen space.
Medium-width device Navigation rail or dismissible drawer Balances navigation access with content space.
Large tablet or desktop Permanent drawer or navigation rail Can keep navigation visible while allowing a wider content area.

For adaptive applications, consider the Material 3 navigation suite APIs where appropriate. These can help select a navigation presentation based on window size instead of maintaining separate, duplicated navigation implementations.


18. Common Errors and Troubleshooting

Problem Possible Cause Solution
Unresolved reference for an icon The icon dependency is missing or the icon is not available in the selected artifact. Add the appropriate dependency or use an available icon.
Drawer does not open The menu callback is missing, or the button uses a different drawer state. Use the same remembered DrawerState and launch its open function in a coroutine.
Drawer does not close after selection The click handler updates the destination but does not close the drawer. Call drawerState.close() inside a coroutine.
Selected bottom item does not update The selected state is not updated, or it is not derived from the current route. Update the state or observe the navigation back stack.
Content is covered by the bottom bar Scaffold content padding is ignored. Apply the innerPadding supplied by Scaffold.
Wrong screen appears after selection Routes or destination-to-screen mappings are incorrect. Verify route constants and navigation graph entries.
Back button behaves unexpectedly Navigation actions are creating unnecessary back-stack entries. Review navigation options, start destinations, and back-stack policy.
TopAppBar produces an opt-in warning The installed Material 3 version marks the API as experimental. Follow the opt-in requirements for that specific library version.
Drawer content is too long Items exceed the available vertical space. Use a scrollable layout and group destinations logically.

19. Testing Bottom Navigation and Drawers

Navigation should be tested for both visual correctness and functional behavior.

Manual Testing Checklist

  • Verify that all bottom navigation items are visible and readable.
  • Tap each bottom navigation item and confirm that the correct section appears.
  • Confirm that the selected item changes when the destination changes.
  • Open the drawer using its menu button.
  • Verify that the drawer can be dismissed using expected interactions.
  • Select each drawer destination and verify the result.
  • Confirm that the drawer closes after a selection when appropriate.
  • Test Back behavior and ensure that the application does not return to an unexpected screen.
  • Rotate the device or recreate the activity to verify state restoration where expected.
  • Test on different screen sizes and with accessibility tools.

Example UI Test Idea

// Conceptual Compose UI test:
composeTestRule
    .onNodeWithText("Search")
    .performClick()

composeTestRule
    .onNodeWithText("Search")
    .assertExists()

This is a simplified illustration. In a real test, ensure the selected label or destination content uniquely identifies the expected screen, and use the appropriate Compose UI testing dependencies and test rule setup.


20. Practical Exercise

Project: Learning App Navigation Interface

Create a learning application with a bottom navigation bar and a modal navigation drawer.

Requirements

  1. Create Home, My Courses, and Profile destinations in the bottom bar.
  2. Add Dashboard, Certificates, Downloads, Help, and Settings to the drawer.
  3. Use Material 3 navigation components.
  4. Highlight the selected destination.
  5. Close the drawer after a user selects an item.
  6. Display the correct content for each destination.
  7. Use Navigation Compose if the project requires real screen navigation and back-stack management.
  8. Ensure content is not hidden behind system bars or the bottom navigation bar.
  9. Test the interface on at least two screen sizes.

Expected Result

The app should allow users to access its most important destinations directly from the bottom bar and find additional features through the drawer. Navigation should be predictable, selected states should remain consistent, and Back behavior should be understandable.


21. Interview Questions and Answers

Q1. What is bottom navigation in Android?

Answer: Bottom navigation is a UI component placed at the bottom of a screen that allows users to switch between a small number of important application destinations.

Q2. What is a navigation drawer?

Answer: A navigation drawer is a panel that contains navigation options and can slide in from the side of the application or remain visible in certain layouts.

Q3. Which composable creates a bottom navigation bar in Material 3?

Answer: The NavigationBar composable creates the container, while NavigationBarItem represents individual destinations.

Q4. Which composable creates a modal navigation drawer?

Answer: ModalNavigationDrawer creates a modal drawer that can appear over the main content.

Q5. What is the purpose of NavigationBarItem's selected parameter?

Answer: It indicates whether the item corresponds to the currently selected destination and controls its selected visual appearance.

Q6. Why is rememberSaveable useful for a selected tab?

Answer: It can preserve simple saveable UI state through supported activity recreation and saved-state restoration scenarios.

Q7. Why is rememberCoroutineScope used with DrawerState?

Answer: The drawer's open() and close() operations are suspend functions. A coroutine scope allows them to be called from UI event handlers.

Q8. What is the difference between remember and rememberSaveable?

Answer: remember retains a value across recompositions. rememberSaveable can also restore supported state after activity recreation when saved state is available.

Q9. When should a navigation drawer be preferred over bottom navigation?

Answer: A drawer is often preferable when an application has many destinations or needs grouped secondary options that would overcrowd a bottom bar.

Q10. What is Navigation Compose?

Answer: Navigation Compose is the AndroidX navigation integration for Compose applications. It provides tools such as a navigation controller, navigation host, and composable destinations.

Q11. Why should Scaffold's inner padding be applied to content?

Answer: It helps prevent the main content from being obscured by Scaffold-managed elements such as app bars and bottom bars.

Q12. Can bottom navigation and a navigation drawer be used together?

Answer: Yes. They can serve different purposes, provided the destination hierarchy is clear and duplicated navigation options do not confuse users.

Q13. How can the selected drawer item stay synchronized with the displayed screen?

Answer: Derive its selected state from the current navigation destination or a single shared state source rather than maintaining unrelated selection values.

Q14. What should be considered when implementing navigation on tablets?

Answer: Consider window width, orientation, touch accessibility, and whether a navigation rail or permanent drawer would make better use of the available space.

Q15. What is a common navigation implementation mistake?

Answer: A common mistake is changing the selected indicator without changing the actual screen, which can cause the UI to show inconsistent navigation state.


22. Quick Revision Notes

  • NavigationBar creates the bottom navigation container.
  • NavigationBarItem represents a bottom navigation destination.
  • selected controls the item's selected state.
  • onClick handles navigation item interactions.
  • ModalNavigationDrawer creates a modal navigation drawer.
  • ModalDrawerSheet provides the drawer's visual surface.
  • NavigationDrawerItem displays a destination inside the drawer.
  • rememberDrawerState stores the drawer's open or closed state.
  • rememberCoroutineScope helps invoke drawer suspend functions in response to UI events.
  • Scaffold organizes app bars, bottom bars, and content.
  • rememberSaveable can preserve simple saveable selection state.
  • Navigation Compose is useful for managing real destinations and back-stack behavior.
  • Bottom navigation is usually best for a few main destinations; drawers can accommodate larger sets of options.
  • Always test selection behavior, Back navigation, accessibility, and responsive layouts.

23. Official Learning Resources

24. Conclusion

Building bottom navigation and navigation drawers is an important skill in Android app development with Jetpack Compose. Bottom navigation gives users quick access to a small set of primary destinations, while navigation drawers organize additional sections in a compact and structured menu.

By using Material 3 components, managing state carefully, applying Scaffold padding, and connecting navigation controls to a consistent navigation architecture, developers can build interfaces that are easy to understand, accessible, and maintainable. Practice both approaches in a sample application and test navigation behavior across different screen sizes before using them in a production project.

Continue Learning: Explore the Android App Development Course, register for a course demo, or visit the JustAcademy website.

Previous Next
whatsapp